package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.srb.base.consts.SrbMqConst;
import com.atguigu.srb.base.dto.SmsDTO;
import com.atguigu.srb.base.result.ResponseEnum;
import com.atguigu.srb.base.util.SrbAssert;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.TransFlow;
import com.atguigu.srb.core.pojo.entity.UserAccount;
import com.atguigu.srb.core.mapper.UserAccountMapper;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.service.TransFlowService;
import com.atguigu.srb.core.service.UserAccountService;
import com.atguigu.srb.core.service.UserInfoService;
import com.atguigu.srb.core.util.LendNoUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 用户账户 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2024-10-11
 */
@Service
@Slf4j
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {


    @Resource
    UserInfoService userInfoService;
    @Resource
    TransFlowService transFlowService;

    @Resource
    UserAccountService userAccountService;
    @Resource
    RabbitTemplate rabbitTemplate;

    @Override
    public String charge(BigDecimal amount, String token) {
        //1、验证登录状态+验证用户账户状态（锁定  绑定hfb..）
        UserInfo userInfo = userInfoService.checkUserStatus(token);
        //2、校验充值金额
        SrbAssert.AssertTrue(amount.doubleValue()>0, ResponseEnum.NOT_CHARGE_ERROR);
        //3、准备表单参数
        Map<String,Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);//商户id
        String chargeNo = LendNoUtils.getChargeNo();
        params.put("agentBillNo", chargeNo);//商户充值单号：必须唯一  对账
        params.put("bindCode",userInfo.getBindCode());//会员在hfb开通的账户的唯一编号
        params.put("chargeAmt",amount);//用户充值提现不扣除手续费：借款人借款成功扣除一部分、投资人回款时扣除一部分手续费
        params.put("feeAmt","0");//手续费
        params.put("notifyUrl",HfbConst.RECHARGE_NOTIFY_URL);//异步回调
        params.put("returnUrl",HfbConst.RECHARGE_RETURN_URL);//同步地址
        params.put("timestamp",System.currentTimeMillis());
        params.put("sign", RequestHelper.getSign(params));

        //保存交易日志
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userInfo.getId());
        transFlow.setUserName(userInfo.getName());
        transFlow.setTransNo(chargeNo);
        transFlow.setStatus(0);
        transFlow.setTransType(1);//1:充值
        transFlow.setTransTypeName("充值");
        transFlow.setTransAmount(amount);
        transFlowService.save(transFlow);

        //返回表单
        return FormHelper.buildForm(HfbConst.RECHARGE_URL,params);
    }


    //保证幂等性：保证携带了相同唯一token的请求 业务方法只处理一次即可
    //数据库： 第一步生成充值表单时将充值日志存到数据库日志表中（transflow表）状态设置为0（表示交易未完成）
            // 第二步回调时 如果transflow表中存在对应的日志且状态为0表示第一次回调，执行业务代码并更新日志状态为1
    @Override
    public String rechargeNotify(Map<String, Object> map) {
        //1、验签
        boolean signEquals = RequestHelper.isSignEquals(map);
//        SrbAssert.AssertTrue(signEquals,ResponseEnum.WX_LOGIN_CALLBACK_TOKEN_ERROR);
        //当前接口由hfb调用，所以要返回hfb看的懂得结果
        if (!signEquals){
            log.error("充值回调失败:签名错误{}", JSON.toJSONString(map));
            return "fail";
        }
        //验证hfb处理业务的状态
        String resultCode = map.get("resultCode").toString();
        String resultMsg = map.get("resultMsg").toString();
        String agentBillNo = map.get("agentBillNo").toString();
        String bindCode = map.get("bindCode").toString();
        String chargeAmt = map.get("chargeAmt").toString();
        String hyFee = map.get("hyFee").toString();
        String mchFee = map.get("mchFee").toString();
//        SrbAssert.AssertTrue("0001".equals(resultCode),ResponseEnum.WEIXIN_CALLBACK_PARAM_ERROR);
        if (!"0001".equals(resultCode)){
            log.error("充值回调失败:状态码错误{}", JSON.toJSONString(map));
            return "fail";
        }

        //3、验证充值金额+幂等性验证
        //根据agentBillNo查询交易日志
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class)
                .eq(TransFlow::getTransNo, agentBillNo));
        if (transFlow==null){
            //交易号不存在
            log.error("充值回调失败:交易编号错误{}", JSON.toJSONString(map));
            return "fail";
        }
        if (transFlow.getStatus()==1){
            //交易号存在 并且已完成
            return "success";
        }

        //交易号存在 并且交易未完成
        //3.1 校验充值金额
        if (transFlow.getTransAmount().compareTo(new BigDecimal(chargeAmt))!=0){
            log.error("充值回调失败:hfb充值金额错误{}", JSON.toJSONString(map));
            return "fail";
        }


        //4、更新用户账户余额
        //bindCode在userInfo表中对应唯一的一个用户
        //userAccount表没有存bindCode，绑定的是userId
        //根据bindCode查询userId，根据userId跟新userAccount账户金额在之前基础上+本次充值金额
        userAccountService.updateAmountByBindCode(new BigDecimal(chargeAmt),bindCode);
        //更新交易日志
        transFlow.setStatus(1);
        transFlow.setCreateTime(null);
        transFlow.setUpdateTime(null);//修改update值为null，触发自动填充
        transFlow.setMemo(JSON.toJSONString(map));
        transFlowService.updateById(transFlow);

        //发送 充值成功的消息： 消息内容
        UserInfo userInfo = userInfoService.getById(transFlow.getUserId());
        SmsDTO smsDTO = new SmsDTO();
        smsDTO.setMobile(userInfo.getMobile());
        smsDTO.setType(3);
        smsDTO.setParams(Arrays.asList("cz"));
        rabbitTemplate.convertAndSend(SrbMqConst.SMS_EXCHANGE,
                //发送短信是 需要使用不同的短信模板
                //type=3表示充值成功
                SrbMqConst.SMS_ROUTING_KEY, smsDTO);

        //返回结果
        return "success";
    }

    @Override
    public boolean updateAmountByBindCode(BigDecimal amount, String bindCode) {
        return baseMapper.updateAmountByBindCode(amount,bindCode);
    }

    @Override
    public UserAccount getUserAccountByUserId(Long userId) {
        return this.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId,userId));
    }

    @Override
    public boolean updateInvestAmountByUserId(Long userId, String voteAmt) {
        return baseMapper.updateInvestAmountByUserId(userId,voteAmt);
    }

    @Override
    public boolean updateFreezeAmountByUserId(Long investUserId, BigDecimal investAmount) {
        return baseMapper.updateFreezeAmountByUserId(investUserId,investAmount);
    }

    @Override
    public String withDraw(String token, BigDecimal amount) {
        //检查用户的状态
        UserInfo userInfo = userInfoService.checkUserStatus(token);
        //提现金额必须大于0
        SrbAssert.AssertTrue(amount.compareTo(new BigDecimal(0))>0,ResponseEnum.WITH_DRAW_ACCOUNT_TO_LOW_ERROR);
        //提现金额不能超过自己的账户余额
        UserAccount userAccount = this.getUserAccountByUserId(userInfo.getId());
        SrbAssert.AssertTrue(userAccount.getAmount().compareTo(amount)>=0, ResponseEnum.WITH_DRAW_ACCOUNT_ERROR);
        //创建自动提交的表单字符串


        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);
        String withdrawNo = LendNoUtils.getWithdrawNo();
        params.put("agentBillNo", withdrawNo);//本次提现操作的单号
        params.put("bindCode",userInfo.getBindCode());//提现用户的hfb账户的bindCode
        params.put("fetchAmt",amount);
        params.put("feeAmt","0");
        params.put("returnUrl",HfbConst.WITHDRAW_RETURN_URL);
        params.put("notifyUrl",HfbConst.WITHDRAW_NOTIFY_URL);
        params.put("timestamp",System.currentTimeMillis());
        params.put("sign",RequestHelper.getSign(params));


        //=============保存提现日志
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userInfo.getId());//提现人id
        transFlow.setUserName(userInfo.getName());//提现人姓名
        transFlow.setTransNo(withdrawNo);//提现编号
        transFlow.setTransType(2);//2:提现
        transFlow.setTransTypeName("提现");
        transFlow.setTransAmount(amount);
        transFlow.setStatus(0);
        transFlowService.save(transFlow);

        return FormHelper.buildForm(HfbConst.WITHDRAW_URL,params);
    }

    @Override
    public String notifyWithdraw(Map<String, Object> map) {
        //1、校验hfb返回数据的安全（签名、状态码）
        boolean flag = RequestHelper.isSignEquals(map);
        if (!flag){
            log.error("提现回调失败，签名错误：{}",JSON.toJSONString(map));
            return "fail";
        }
        if(!"0001".equals(map.get("resultCode"))){
            log.error("提现回调失败，汇付宝业务处理错误：{}",JSON.toJSONString(map));
            return "fail";
        }

        //2、校验提现金额、幂等性
        String agentBillNo = map.get("agentBillNo").toString();
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class)
                .eq(TransFlow::getTransNo, agentBillNo));
        if (transFlow.getStatus()==1){
            return "success";
        }
        BigDecimal bigDecimal = new BigDecimal(map.get("fetchAmt").toString());
        if (transFlow.getTransAmount().compareTo(bigDecimal)!=0){
            log.error("提现回调失败，提现金额错误：{}",JSON.toJSONString(map));
            return "fail";
        }

        //3、更新用户账户余额、日志
            //更新日志状态
        transFlow.setMemo(JSON.toJSONString(map));
        transFlow.setStatus(1);
        transFlow.setUpdateTime(null);
        transFlowService.updateById(transFlow);

            //扣除账户提现的金额
        Long userId = transFlow.getUserId();
        UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, userId));
        userAccount.setAmount(userAccount.getAmount().subtract(bigDecimal));
        userAccountService.updateById(userAccount);

        //发送短信通知提现成功：异步解耦合（rabbitmq）
        UserInfo userInfo = userInfoService.getById(transFlow.getUserId());
        SmsDTO smsDTO = new SmsDTO();
        smsDTO.setMobile(userInfo.getMobile());
        smsDTO.setType(4);
        smsDTO.setParams(Arrays.asList("tx"+transFlow.getTransAmount()));

        rabbitTemplate.convertAndSend(SrbMqConst.SMS_EXCHANGE,
                //发送短信是 需要使用不同的短信模板
                //type=3表示充值成功
                SrbMqConst.SMS_ROUTING_KEY, smsDTO);

        return "success";
    }
}
